﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp;
using Abp.Auditing;
using Abp.Authorization;
using Abp.BackgroundJobs;
using Abp.Runtime.Caching;
using Abp.UI;
using Microsoft.AspNetCore.Identity;
using pandx.Mulan.Authorization.Users.Profile.Dto;
using pandx.Mulan.Storage;

namespace pandx.Mulan.Authorization.Users.Profile
{
    public class ProfileAppService : MulanAppServiceBase, IProfileAppService
    {
        private const int MaxProfilePictureBytes = 1024 * 1024 * 5;
        private readonly IBinaryObjectManager _binaryObjectManager;
        private readonly ICacheManager _cacheManager;
        private readonly ITempFileCacheManager _tempFileCacheManager;
        private readonly IBackgroundJobManager _backgroundJobManager;

        private readonly UserManager _userManager;
        public ProfileAppService(IBinaryObjectManager binaryObjectManager,
            ICacheManager cacheManager,
            ITempFileCacheManager tempFileCacheManager,
            IBackgroundJobManager backgroundJobManager,
            UserManager userManager)
        {
            _binaryObjectManager = binaryObjectManager;
            _cacheManager = cacheManager;
            _tempFileCacheManager = tempFileCacheManager;
            _backgroundJobManager = backgroundJobManager;
            _userManager = userManager;
        }
        [AbpAuthorize]
        [DisableAuditing]
        public async Task<CurrentUserProfileEditDto> GetCurrentUserProfileForEdit()
        {
            var user = await GetCurrentUserAsync();
            var userProfileEditDto = ObjectMapper.Map<CurrentUserProfileEditDto>(user);
            return userProfileEditDto;
        }
        [AbpAuthorize]
        public async Task UpdateCurrentUserProfile(CurrentUserProfileEditDto input)
        {
            var user = await GetCurrentUserAsync();
            ObjectMapper.Map(input, user);
            CheckErrors(await _userManager.UpdateAsync(user));
        }
        [AbpAuthorize]
        public async Task ChangePassword(ChangePasswordInput input)
        {
            await _userManager.InitializeOptionsAsync(AbpSession.TenantId);
            var user = await GetCurrentUserAsync();
            if (await _userManager.CheckPasswordAsync(user, input.CurrentPassword))
            {
                CheckErrors(await _userManager.ChangePasswordAsync(user, input.NewPassword));
            }
            else
            {
                throw new UserFriendlyException("输入的旧密码错误");
            }
        }
        [AbpAuthorize]
        public async Task UpdateProfilePicture(UpdateProfilePictureInput input)
        {
            byte[] finalBytes;
            var bytes = _tempFileCacheManager.GetFile(input.FileToken);
            if (bytes == null)
            {
                throw new UserFriendlyException("没有在缓冲中发现请求的图像");
            }

            if (bytes.Length > MaxProfilePictureBytes)
            {
                throw new UserFriendlyException("裁剪图像大小超出 5MB 的限制");
            }

            var user = await GetCurrentUserAsync();
            if (user.ProfilePictureId.HasValue)
            {
                await _binaryObjectManager.DeleteAsync(user.ProfilePictureId.Value);
            }
            
            using (var bitmap = new Bitmap(new MemoryStream(bytes)))
            {
                var newBitmap = bitmap.Width >= bitmap.Height
                    ? new Bitmap(bitmap, bitmap.Width * 150 / bitmap.Height, 150)
                    : new Bitmap(bitmap, 150, bitmap.Height * 150 / bitmap.Width);
                var bitmapCrop = newBitmap.Clone(new Rectangle(0, 0, 150, 150), newBitmap.PixelFormat);


                await using var stream = new MemoryStream();
                bitmapCrop.Save(stream, bitmap.RawFormat);
                finalBytes = stream.ToArray();
            }

            var savedFile = new BinaryObject(AbpSession.TenantId, finalBytes,
                $"用户{user.Name}（{user.UserName}）的头像，上传于{DateTime.Now}");
            await _binaryObjectManager.SaveAsync(savedFile);

            user.ProfilePictureId = savedFile.Id;
        }
        [AbpAuthorize]
        [DisableAuditing]
        public async Task<GetProfilePictureOutput> GetProfilePicture()
        {
            var user = await GetCurrentUserAsync();
            if (user.ProfilePictureId == null)
            {
                return new GetProfilePictureOutput(string.Empty);
            }

            return await GetProfilePictureByIdInternal(user.ProfilePictureId.Value);
        }
        [AbpAuthorize]
        public async Task<GetProfilePictureOutput> GetProfilePictureByUser(long userId)
        {
            var userIdentifier = new UserIdentifier(AbpSession.TenantId, userId);
            var user = await _userManager.GetUserOrNullAsync(userIdentifier);
            if (user == null)
            {
                return new GetProfilePictureOutput(string.Empty);
            }

            if (user.ProfilePictureId == null)
            {
                return new GetProfilePictureOutput(string.Empty);
            }

            return await GetProfilePictureByIdInternal(user.ProfilePictureId.Value);
        }
        [AbpAuthorize]
        public async Task<GetProfilePictureOutput> GetProfilePictureByUserName(string username)
        {
            var user = await _userManager.FindByNameAsync(username);
            if (user == null)
            {
                return new GetProfilePictureOutput(string.Empty);
            }

            if (user.ProfilePictureId == null)
            {
                return new GetProfilePictureOutput(string.Empty);
            }

            return await GetProfilePictureByIdInternal(user.ProfilePictureId.Value);
        }


        private async Task<byte[]> GetProfilePictureByIdOrNull(Guid profilePictureId)
        {
            var file = await _binaryObjectManager.GetOrNullAsync(profilePictureId);
            if (file == null)
            {
                return null;
            }

            return file.Bytes;
        }


        private async Task<GetProfilePictureOutput> GetProfilePictureByIdInternal(Guid profilePictureId)
        {
            var bytes = await GetProfilePictureByIdOrNull(profilePictureId);
            if (bytes == null)
            {
                return new GetProfilePictureOutput(string.Empty);
            }

            return new GetProfilePictureOutput(Convert.ToBase64String(bytes));
        }
    }
}
